不曉得大家知道前陣子網路上非常紅的 Cursor IDE 嗎?它是基於 VS Code 改造而來,並高度整合了 AI 的一個 IDE。它不像是 Github Copilot 那樣只是一個 Extension 的 AI,而是用 VS Code 的開源程式碼將 AI 整合進編輯器中。不斷聽說非常的厲害,今天就來玩看看。這篇不是教學,比較像是我第一次使用的經驗紀錄。
今天的目標是做一個 HTML5 的前端網頁,因為不想要牽扯太多安裝,就先不使用 NodeJS 了,有興趣的可以自行叫 Cursor 做。
安裝看起來也很簡單,應該不需要多教學,我電腦內的 Cursor 也是我編寫邊安裝的,就不教學了。
為了不失焦,我們先建立一些檔案結構。一樣用 Cursor 開啟我們的 microservices 專案資料夾,並在 /src/ 內新增 WebApp
資料夾。
再來給他實作的目標檔案,建立 index.html
、dashboard.html
,目的是想讓 Cursor 在這裡實作。
之後餵給它我們當前微服務 BFF 的介面資料,建立 swagger.json
和 sample.http
檔案。
從 BFF Gateway Swagger 的標頭取得你的 Swagger 內容,並貼到 swagger.json
中。
還記得我們之前在 BFF Gateway 做了一個 BFF.Gateway.http
的測試檔案嗎?我們利用這個檔案執行每一個 endpoint 的測試,並把 Response 寫進裡面給 Cursor 參考。主要是讓 AI 知道我們的資料結構。
我做出來的大致如下:
@HostAddress = http://localhost:5282
### AccountController - Register
POST {{HostAddress}}/api/Account/register
Content-Type: application/json
{
"firstName": "Alice",
"lastName": "Smith",
"email": "alice.smith@mail.com",
"password": "1234"
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:31:18 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
#
# {
# "id": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
# "firstName": "Alice",
# "lastName": "Smith",
# "email": "alice.smith@mail.com",
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDkzNzgsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJmZGRhYzc3OS03NjYzLTQ0YmEtODZhZS1kMWZiMTY4ODYzZDUiLCJpYXQiOjE3Mjg1NDkwNzgsIm5iZiI6MTcyODU0OTA3OH0.fgcmySjSwM8hh5QZApPY21ukO5qBz6KTngqYdcMbtcc"
# }
### AccountController - Login
POST {{HostAddress}}/api/Account/login
Content-Type: application/json
{
"email": "alice.smith@mail.com",
"password": "1234"
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:32:29 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
# "firstName": "Alice",
# "lastName": "Smith",
# "email": "alice.smith@mail.com",
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY"
# }
### TodoListController - Create Todo List
POST {{HostAddress}}/api/TodoList/create
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY
Content-Type: application/json
{
"userId": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
"name": "Work",
"description": "Work notes"
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:33:23 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "929556e7-5414-46c2-809c-7fae000270c5",
# "userId": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
# "name": "Work",
# "description": "Work notes",
# "status": "Active"
# }
### TodoListController - Remove Todo List
DELETE {{HostAddress}}/api/TodoList/remove/929556e7-5414-46c2-809c-7fae000270c5
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:37:16 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "929556e7-5414-46c2-809c-7fae000270c5",
# "userId": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
# "name": "Work",
# "description": "Work notes",
# "status": "Removed"
# }
### TodoItemController - Create Todo Item
POST {{HostAddress}}/api/TodoItem/create
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY
Content-Type: application/json
{
"listId": "929556e7-5414-46c2-809c-7fae000270c5",
"content": "Good Job"
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:34:28 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "listId": "929556e7-5414-46c2-809c-7fae000270c5",
# "content": "Good Job",
# "status": "Todo",
# "color": "#FFFF00"
# }
### TodoItemController - Finish Todo Item
POST {{HostAddress}}/api/TodoItem/finish
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY
Content-Type: application/json
{
"id": "3c609cbe-9a54-4e95-af36-9330c45e22c8"
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:35:19 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "listId": "929556e7-5414-46c2-809c-7fae000270c5",
# "content": "Good Job",
# "status": "Finished",
# "color": "#008000"
# }
### TodoItemController - Remove Todo Item
DELETE {{HostAddress}}/api/TodoItem/remove/3c609cbe-9a54-4e95-af36-9330c45e22c8
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0b2RvLWF1ZGllbmNlIiwiaXNzIjoidG9kby1pc3N1ZXIiLCJleHAiOjE3Mjg1NDk0NTAsInN1YiI6IjlkYmVkMTFlLWQzNTAtNDMzNC05MWVmLWI5ODJhZGZiM2RjOCIsImdpdmVuX25hbWUiOiJBbGljZSIsImZhbWlseV9uYW1lIjoiU21pdGgiLCJqdGkiOiJiMmE5ZGM2My1lNjA0LTQ5MmQtOTA2Mi05YWU4MjFmYTRjN2EiLCJpYXQiOjE3Mjg1NDkxNTAsIm5iZiI6MTcyODU0OTE1MH0.GQGNeLzh-zxbTR5cxBhH4cSfWRfCE-KJxsHF5C_ixfY
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:36:33 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "listId": "929556e7-5414-46c2-809c-7fae000270c5",
# "content": "Good Job",
# "status": "Removed",
# "color": "#808080"
# }
### GraphQLController - Query User
POST {{HostAddress}}/api/GraphQL/query
Content-Type: application/json
{
"query": "query ($userId: String!) { userById(userId: $userId) { id firstName lastName email todoListDtos { id name description status todoItemDtos { id content state color } } }}",
"variables": {
"userId": "9dbed11e-d350-4334-91ef-b982adfb3dc8"
}
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:38:52 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "userById": {
# "id": "9dbed11e-d350-4334-91ef-b982adfb3dc8",
# "firstName": "Alice",
# "lastName": "Smith",
# "email": "alice.smith@mail.com",
# "todoListDtos": [
# {
# "id": "54efc5a9-191c-4828-bbcd-177b1f351afa",
# "name": "Default",
# "description": "Default",
# "status": "ACTIVE",
# "todoItemDtos": []
# },
# {
# "id": "929556e7-5414-46c2-809c-7fae000270c5",
# "name": "Work",
# "description": "Work notes",
# "status": "REMOVED",
# "todoItemDtos": [
# {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "content": "Good Job",
# "state": "REMOVED",
# "color": "#808080"
# }
# ]
# }
# ]
# }
# }
### GraphQLController - Query Todo List
POST {{HostAddress}}/api/GraphQL/query
Content-Type: application/json
{
"query": "query ($listId: String!) { todoListById(listId: $listId) { id name description todoItemDtos { id content state color } }}",
"variables": {
"listId": "929556e7-5414-46c2-809c-7fae000270c5"
}
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:40:25 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "todoListById": {
# "id": "929556e7-5414-46c2-809c-7fae000270c5",
# "name": "Work",
# "description": "Work notes",
# "todoItemDtos": [
# {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "content": "Good Job",
# "state": "REMOVED",
# "color": "#808080"
# }
# ]
# }
# }
### GraphQLController - Query Todo Item
POST {{HostAddress}}/api/GraphQL/query
Content-Type: application/json
{
"query": "query ($itemId: String!) { todoItemById(itemId: $itemId) { id content state color }}",
"variables": {
"itemId": "3c609cbe-9a54-4e95-af36-9330c45e22c8"
}
}
# Response
# HTTP/1.1 200 OK
# Connection: close
# Content-Type: application/json; charset=utf-8
# Date: Thu, 10 Oct 2024 08:41:37 GMT
# Server: Kestrel
# Transfer-Encoding: chunked
# {
# "todoItemById": {
# "id": "3c609cbe-9a54-4e95-af36-9330c45e22c8",
# "content": "Good Job",
# "state": "REMOVED",
# "color": "#808080"
# }
# }
接下來按下 ctrl
+ L
來開啟 Cursor 的對話視窗。
加入相關要給 Cursor 參考的檔案如下:
然後用自然語言跟他說一些細節,自身經驗來說,跟 AI 講話細節最好條列式,所以我這樣跟他說我的需求
請在 WebApp 資料夾內開發一個使用 HTML5 的 Todo List 前端應用程式,功能包含:
註冊/登入/登出、建立/刪除 Todo List、新增/完成/刪除 Todo Item,請依照以下需求進行開發:
1. 使用我提供的 Gateway 可參考 swagger.json,網址為 http://localhost:5282,API 文件請完全按照 sample.http 中的 Request/Response 範例格式。
2. 請完全基於 HTML5 開發,不使用 Node.js。
3. 設計需具備現代感,可使用 BootStrap 框架。
4. 網站分為兩個主要頁面:
- index.html:用於註冊和登入頁面。
- dashboard.html:主控台頁面,登入後顯示 Todo List。
5. 在登入後的 Dashboard 頁面,右上角需顯示登出按鈕及當前登入使用者名稱。
6. 在使用者成功登入或註冊後,伺服器會回傳 JWT,請參照 sample.http,並將 JWT 存儲,後續請求(如建立 TodoList、TodoItem 等操作)需附帶 `Authorization: Bearer JWT`。
7. Todo List 有兩種狀態:ACTIVE 和 REMOVED。
8. 使用 GraphQL Query 的查詢不需要 Authorization,並嚴格按照 sample.http 的 body 來帶入 Variable。
9. Todo Item 的 Response 會包含顏色屬性,請將此顏色套用至每個 Todo Item 上,並使用卡片(Card)風格設計顯示。
10. Todo Item 有三種狀態
- TODO :可以完成或刪除。
- FINISHED:只能刪除。
- REMOVED:無法進行任何操作。
11. 刪除操作後,對應的 Todo List 項目應直接隱藏,Todo Item 則是留在畫面上但無法操作之。
12. 請處理好所有的 Error Handling。
身為一個初次使用的小白,聽了這麼多 Cursor 的神話故事,我無條件相信它會做好的,所以不管結果如何,我接下來都會無條件的按下 Accept 跟 Apply。
程式碼的部分按下 Accept 後 Apply 就會自動幫你逐行改 Code。
這樣 index 就自己做好了。接下來換 dashboard。
用 VS Code 的 Live Preview 來看結果。
看起來成果不錯!登入看看。
成功了。
嘗試做一些操作看來都沒有問題。
登出註冊看看。
沒意外的也都通過測試。
甚至連防呆都有做!
我們針對不滿意的地方繼續與它互動,順便給它一點挑戰。在程式碼內按下 ctrl
+ k
就可以叫出互動對話。
像這樣,會給你一個前後對比,確認無誤後確定修改即可。
結果蠻讓我驚豔的。
我後續還請 Cursor 做了一些小改進:
新增 Brand Image,並讓 Todo List Description 顯示出來。
JWT 過期自動登出。
特別跑去 Todo.Domain 改了顏色。
TodoItemState.Todo => "#FFDD00", // 黃色
TodoItemState.Finished => "#30CC30", // 綠色
TodoItemState.Removed => "#666666", // 灰色
成果圖。
這篇就先到這邊吧。我自己試玩後都想繳月費了!我應該會找時間測試更多種技術來決定要不要花這錢,真心不錯!